package com.example.distributedemo.service;

import com.example.distributedemo.dao.OrderItemMapper;
import com.example.distributedemo.dao.OrderMapper;
import com.example.distributedemo.dao.ProductMapper;
import com.example.distributedemo.model.Order;
import com.example.distributedemo.model.OrderItem;
import com.example.distributedemo.model.Product;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

@Service
@Slf4j
public class OrderService {

    @Resource
    private OrderMapper orderMapper;
    @Resource
    private OrderItemMapper orderItemMapper;
    @Resource
    private ProductMapper productMapper;
    // 购买商品id
    private int purchaseProductId = 100100;
    // 购买商品数量
    private int purchaseProductNum = 1;

    /**
     * 初始代码
     * @return
     * @throws Exception
     */
    @Transactional(rollbackFor = Exception.class)
    public Integer createOrder1() throws Exception {
        Product product = productMapper.selectByPrimaryKey(purchaseProductId);
        if (product == null) {
            throw new Exception("购买商品："+purchaseProductId+"不存在");
        }

        // 商品当前库存
        Integer currentCount = product.getCount();
        // 校验库存
        if (purchaseProductNum > currentCount) {
            throw new Exception("商品"+purchaseProductId+"仅剩"+currentCount+"件，无法购买");
        }

        // 计算剩余库存
        Integer leftCount = currentCount - purchaseProductNum;
        // 更新库存
        product.setCount(leftCount);
        product.setUpdateTime(new Date());
        product.setUpdateUser("xxx");
        productMapper.updateByPrimaryKeySelective(product);

        Order order = new Order();
        order.setOrderAmount(product.getPrice().multiply(new BigDecimal(purchaseProductNum)));
        order.setOrderStatus(1);//待处理
        order.setReceiverName("xxx");
        order.setReceiverMobile("13311112222");
        order.setCreateTime(new Date());
        order.setCreateUser("xxx");
        order.setUpdateTime(new Date());
        order.setUpdateUser("xxx");
        orderMapper.insertSelective(order);

        OrderItem orderItem = new OrderItem();
        orderItem.setOrderId(order.getId());
        orderItem.setProductId(product.getId());
        orderItem.setPurchasePrice(product.getPrice());
        orderItem.setPurchaseNum(purchaseProductNum);
        orderItem.setCreateUser("xxx");
        orderItem.setCreateTime(new Date());
        orderItem.setUpdateTime(new Date());
        orderItem.setUpdateUser("xxx");
        orderItemMapper.insertSelective(orderItem);
        return order.getId();
    }

    /**
     * 解决超卖现象一
     * @return
     * @throws Exception
     */
    @Transactional(rollbackFor = Exception.class)
    public Integer createOrder2() throws Exception {
        Product product = productMapper.selectByPrimaryKey(purchaseProductId);
        if (product == null) {
            throw new Exception("购买商品："+purchaseProductId+"不存在");
        }

        // 商品当前库存
        Integer currentCount = product.getCount();
        // 校验库存
        if (purchaseProductNum > currentCount) {
            throw new Exception("商品"+purchaseProductId+"仅剩"+currentCount+"件，无法购买");
        }

//        // 计算剩余库存
//        Integer leftCount = currentCount - purchaseProductNum;
//        // 更新库存
//        product.setCount(leftCount);
//        product.setUpdateTime(new Date());
//        product.setUpdateUser("xxx");
//        productMapper.updateByPrimaryKeySelective(product);

        // 超卖现象一：解决方法
        productMapper.updateProductCount(purchaseProductNum, "xxx", new Date(), product.getId());

        Order order = new Order();
        order.setOrderAmount(product.getPrice().multiply(new BigDecimal(purchaseProductNum)));
        order.setOrderStatus(1);//待处理
        order.setReceiverName("xxx");
        order.setReceiverMobile("13311112222");
        order.setCreateTime(new Date());
        order.setCreateUser("xxx");
        order.setUpdateTime(new Date());
        order.setUpdateUser("xxx");
        orderMapper.insertSelective(order);

        OrderItem orderItem = new OrderItem();
        orderItem.setOrderId(order.getId());
        orderItem.setProductId(product.getId());
        orderItem.setPurchasePrice(product.getPrice());
        orderItem.setPurchaseNum(purchaseProductNum);
        orderItem.setCreateUser("xxx");
        orderItem.setCreateTime(new Date());
        orderItem.setUpdateTime(new Date());
        orderItem.setUpdateUser("xxx");
        orderItemMapper.insertSelective(orderItem);
        return order.getId();
    }

    /**
     * 解决超卖现象二：基于synchronized锁解决超卖问题（最原始的锁）
     * 依然存在的问题：第一个线程执行完，事务还没有提交，第二个线程就开始执行，此时商品库存还是1，从而第一个和第二个线程都执行了减少库存和创建订单操作，导致库存为-1
     * @Transactional是通过aop的方式执行的，由于spring的aop，会在createOrder方法之前开启事务，之后再加锁，当锁住的代码执行完成后，再提交事务，因此锁住的代码块执行是在事务之内执行的，可以推断在代码块执行完时，事务还未提交，锁已经被释放，此时其他线程拿到锁之后进行锁住的代码块，读取的库存数据不是最新的
     * @return
     * @throws Exception
     */
    @Transactional(rollbackFor = Exception.class)
    public synchronized Integer createOrder3() throws Exception {
        Product product = productMapper.selectByPrimaryKey(purchaseProductId);
        if (product == null) {
            throw new Exception("购买商品："+purchaseProductId+"不存在");
        }

        // 商品当前库存
        Integer currentCount = product.getCount();
        System.out.println(Thread.currentThread().getName() + "库存数：" + currentCount);
        // 校验库存
        if (purchaseProductNum > currentCount) {
            throw new Exception("商品"+purchaseProductId+"仅剩"+currentCount+"件，无法购买");
        }

//        // 计算剩余库存
//        Integer leftCount = currentCount - purchaseProductNum;
//        // 更新库存
//        product.setCount(leftCount);
//        product.setUpdateTime(new Date());
//        product.setUpdateUser("xxx");
//        productMapper.updateByPrimaryKeySelective(product);

        // 超卖现象一：解决方法
        productMapper.updateProductCount(purchaseProductNum, "xxx", new Date(), product.getId());

        Order order = new Order();
        order.setOrderAmount(product.getPrice().multiply(new BigDecimal(purchaseProductNum)));
        order.setOrderStatus(1);//待处理
        order.setReceiverName("xxx");
        order.setReceiverMobile("13311112222");
        order.setCreateTime(new Date());
        order.setCreateUser("xxx");
        order.setUpdateTime(new Date());
        order.setUpdateUser("xxx");
        orderMapper.insertSelective(order);

        OrderItem orderItem = new OrderItem();
        orderItem.setOrderId(order.getId());
        orderItem.setProductId(product.getId());
        orderItem.setPurchasePrice(product.getPrice());
        orderItem.setPurchaseNum(purchaseProductNum);
        orderItem.setCreateUser("xxx");
        orderItem.setCreateTime(new Date());
        orderItem.setUpdateTime(new Date());
        orderItem.setUpdateUser("xxx");
        orderItemMapper.insertSelective(orderItem);
        return order.getId();
    }

    @Autowired
    private PlatformTransactionManager platformTransactionManager;
    @Autowired
    private TransactionDefinition transactionDefinition;

    /**
     * 解决上述方法依然存在的问题，手动控制事务
     * @return
     * @throws Exception
     */
//    @Transactional(rollbackFor = Exception.class)
    public synchronized Integer createOrder4() throws Exception {
        TransactionStatus transaction = platformTransactionManager.getTransaction(transactionDefinition);

        Product product = productMapper.selectByPrimaryKey(purchaseProductId);
        if (product == null) {
            // 事务回滚
            platformTransactionManager.rollback(transaction);
            throw new Exception("购买商品："+purchaseProductId+"不存在");
        }

        // 商品当前库存
        Integer currentCount = product.getCount();
        System.out.println(Thread.currentThread().getName() + "库存数：" + currentCount);
        // 校验库存
        if (purchaseProductNum > currentCount) {
            // 事务回滚
            platformTransactionManager.rollback(transaction);
            throw new Exception("商品"+purchaseProductId+"仅剩"+currentCount+"件，无法购买");
        }

//        // 计算剩余库存
//        Integer leftCount = currentCount - purchaseProductNum;
//        // 更新库存
//        product.setCount(leftCount);
//        product.setUpdateTime(new Date());
//        product.setUpdateUser("xxx");
//        productMapper.updateByPrimaryKeySelective(product);

        // 超卖现象一：解决方法
        productMapper.updateProductCount(purchaseProductNum, "xxx", new Date(), product.getId());

        Order order = new Order();
        order.setOrderAmount(product.getPrice().multiply(new BigDecimal(purchaseProductNum)));
        order.setOrderStatus(1);//待处理
        order.setReceiverName("xxx");
        order.setReceiverMobile("13311112222");
        order.setCreateTime(new Date());
        order.setCreateUser("xxx");
        order.setUpdateTime(new Date());
        order.setUpdateUser("xxx");
        orderMapper.insertSelective(order);

        OrderItem orderItem = new OrderItem();
        orderItem.setOrderId(order.getId());
        orderItem.setProductId(product.getId());
        orderItem.setPurchasePrice(product.getPrice());
        orderItem.setPurchaseNum(purchaseProductNum);
        orderItem.setCreateUser("xxx");
        orderItem.setCreateTime(new Date());
        orderItem.setUpdateTime(new Date());
        orderItem.setUpdateUser("xxx");
        orderItemMapper.insertSelective(orderItem);

        // 事务提交
        platformTransactionManager.commit(transaction);

        return order.getId();
    }

    private Object object = new Object();

    /**
     *  解决超卖现象二：基于Synchronized锁解决超卖问题（块锁）
     * @return
     * @throws Exception
     */
//    @Transactional(rollbackFor = Exception.class)
    public Integer createOrder5() throws Exception {

//        // 块锁写法一：五个线程中谁最先抢到OrderService（单例）这个实例对象锁才能执行块锁里面的内容
//        synchronized (this) {
//
//        }
//
//        // 块锁写法二：新建一个object对象，五个线程中谁最先抢到object对象锁才能执行块锁里面的内容，与块锁写法一同理
//        synchronized (object) {
//
//        }
//
//        // 块锁写法三：类锁，五个线程中谁最先抢到OrderService.class类锁才能执行块锁里面的内容。
//        // 上述两种对象锁的写法都存在这样的问题，有可能OrderService不是单例的，此时可能存在多个OrderService实例对象，锁就存在多个，无法保证锁的唯一性
//        // 但是类锁就不存在这种问题，因为类只能有一个，只能有一个线程获取到
//        synchronized (OrderService.class) {
//
//        }

        Product product = null;

        synchronized (this) {
            TransactionStatus transaction1 = platformTransactionManager.getTransaction(transactionDefinition);
            product = productMapper.selectByPrimaryKey(purchaseProductId);
            if (product == null) {
                // 事务回滚
                platformTransactionManager.rollback(transaction1);
                throw new Exception("购买商品："+purchaseProductId+"不存在");
            }

            // 商品当前库存
            Integer currentCount = product.getCount();
            System.out.println(Thread.currentThread().getName() + "库存数：" + currentCount);
            // 校验库存
            if (purchaseProductNum > currentCount) {
                // 事务回滚
                platformTransactionManager.rollback(transaction1);
                throw new Exception("商品"+purchaseProductId+"仅剩"+currentCount+"件，无法购买");
            }

//        // 计算剩余库存
//        Integer leftCount = currentCount - purchaseProductNum;
//        // 更新库存
//        product.setCount(leftCount);
//        product.setUpdateTime(new Date());
//        product.setUpdateUser("xxx");
//        productMapper.updateByPrimaryKeySelective(product);

            // 超卖现象一：解决方法
            productMapper.updateProductCount(purchaseProductNum, "xxx", new Date(), product.getId());

            // 事务提交
            platformTransactionManager.commit(transaction1);
        }

        TransactionStatus transaction = platformTransactionManager.getTransaction(transactionDefinition);

        Order order = new Order();
        order.setOrderAmount(product.getPrice().multiply(new BigDecimal(purchaseProductNum)));
        order.setOrderStatus(1);//待处理
        order.setReceiverName("xxx");
        order.setReceiverMobile("13311112222");
        order.setCreateTime(new Date());
        order.setCreateUser("xxx");
        order.setUpdateTime(new Date());
        order.setUpdateUser("xxx");
        orderMapper.insertSelective(order);

        OrderItem orderItem = new OrderItem();
        orderItem.setOrderId(order.getId());
        orderItem.setProductId(product.getId());
        orderItem.setPurchasePrice(product.getPrice());
        orderItem.setPurchaseNum(purchaseProductNum);
        orderItem.setCreateUser("xxx");
        orderItem.setCreateTime(new Date());
        orderItem.setUpdateTime(new Date());
        orderItem.setUpdateUser("xxx");
        orderItemMapper.insertSelective(orderItem);

        // 事务提交
        platformTransactionManager.commit(transaction);

        return order.getId();
    }

    private final Lock lock = new ReentrantLock();

    /**
     * 解决超卖现象二：基于ReentrantLock锁（可重入锁）解决超卖问题（并发包中的锁）
     * @return
     * @throws Exception
     */
//    @Transactional(rollbackFor = Exception.class)
    public Integer createOrder6() throws Exception {

        Product product = null;

        lock.lock();

        try {
            TransactionStatus transaction1 = platformTransactionManager.getTransaction(transactionDefinition);
            product = productMapper.selectByPrimaryKey(purchaseProductId);
            if (product == null) {
                // 事务回滚
                platformTransactionManager.rollback(transaction1);
                throw new Exception("购买商品："+purchaseProductId+"不存在");
            }

            // 商品当前库存
            Integer currentCount = product.getCount();
            System.out.println(Thread.currentThread().getName() + "库存数：" + currentCount);
            // 校验库存
            if (purchaseProductNum > currentCount) {
                // 事务回滚
                platformTransactionManager.rollback(transaction1);
                throw new Exception("商品"+purchaseProductId+"仅剩"+currentCount+"件，无法购买");
            }

//        // 计算剩余库存
//        Integer leftCount = currentCount - purchaseProductNum;
//        // 更新库存
//        product.setCount(leftCount);
//        product.setUpdateTime(new Date());
//        product.setUpdateUser("xxx");
//        productMapper.updateByPrimaryKeySelective(product);

            // 超卖现象一：解决方法
            productMapper.updateProductCount(purchaseProductNum, "xxx", new Date(), product.getId());

            // 事务提交
            platformTransactionManager.commit(transaction1);
        } finally {
            lock.unlock();
        }

        TransactionStatus transaction = platformTransactionManager.getTransaction(transactionDefinition);

        Order order = new Order();
        order.setOrderAmount(product.getPrice().multiply(new BigDecimal(purchaseProductNum)));
        order.setOrderStatus(1);//待处理
        order.setReceiverName("xxx");
        order.setReceiverMobile("13311112222");
        order.setCreateTime(new Date());
        order.setCreateUser("xxx");
        order.setUpdateTime(new Date());
        order.setUpdateUser("xxx");
        orderMapper.insertSelective(order);

        OrderItem orderItem = new OrderItem();
        orderItem.setOrderId(order.getId());
        orderItem.setProductId(product.getId());
        orderItem.setPurchasePrice(product.getPrice());
        orderItem.setPurchaseNum(purchaseProductNum);
        orderItem.setCreateUser("xxx");
        orderItem.setCreateTime(new Date());
        orderItem.setUpdateTime(new Date());
        orderItem.setUpdateUser("xxx");
        orderItemMapper.insertSelective(orderItem);

        // 事务提交
        platformTransactionManager.commit(transaction);

        return order.getId();
    }

}
